package com.xplay.xpocker.room;

import com.xplay.xpocker.config.security.CurrentUser;
import com.xplay.xpocker.constant.RoomTypeEnum;
import com.xplay.xpocker.dvo.AbstractRoom;
import com.xplay.xpocker.dvo.RoomInfoInterface;
import com.xplay.xpocker.dvo.RoomRuleInterface;
import com.xplay.xpocker.entity.GameRuMahjongLogs;
import com.xplay.xpocker.entity.GameRuRoom;
import com.xplay.xpocker.norm.AbstractStrategy;
import com.xplay.xpocker.room.mahjong.dto.MahjongRoomInfo;
import com.xplay.xpocker.room.mahjong.dto.MahjongRoomRule;
import com.xplay.xpocker.room.message.MessageToClient;
import com.xplay.xpocker.service.gaming.GameRuRoomService;
import com.xplay.xpocker.observer.ChannelHandlerManager;
import com.xplay.xpocker.util.JacksonStringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 房间行为
 *
 * @param <T> 规则
 * @param <R> 房间信息
 * @param <L> 运行时log日志
 */

@Slf4j
public abstract class RoomStrategy<T extends RoomRuleInterface, R extends RoomInfoInterface, L> extends AbstractStrategy<T, R> {


    private static GameRuRoomService ruRoomService;

    @Autowired
    public void setRuRoomService(GameRuRoomService ruRoomService) {
        RoomStrategy.ruRoomService = ruRoomService;
    }

    private static final ConcurrentHashMap<Integer, RoomStrategy<? extends RoomRuleInterface, ? extends RoomInfoInterface, ?>> roomCreateStrategy = new ConcurrentHashMap<>();


    protected abstract int strategyType();

    @PostConstruct
    public void registerFactory() {
        roomCreateStrategy.put(strategyType(), this);
    }


    public static <M extends RoomRuleInterface, N extends RoomInfoInterface, O> RoomStrategy<M, N, O> getRoomStrategy(int type) {
        return (RoomStrategy<M, N, O>) roomCreateStrategy.get(type);
    }

    public static <M extends RoomRuleInterface, N extends RoomInfoInterface, O> RoomStrategy<M, N, O> getRoomStrategy(String roomCode) {
        GameRuRoom ruRoom = ruRoomService.queryRoomByCode(roomCode);
        RoomTypeEnum roomTypeEnum = RoomTypeEnum.of(ruRoom.getType());
        return RoomStrategy.getRoomStrategy(roomTypeEnum.getParent());
    }

    /**
     * 创建房间事件
     *
     * @param code 房间号
     * @param t    规则
     */

    @Transactional(rollbackFor = Exception.class)
    protected abstract GameRuRoom doCreate(String code, String type, T t);

    @Transactional(rollbackFor = Exception.class)
    public abstract void saveActionLog(List<MessageToClient> message, AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomInfo);

    /**
     * 进入房间事件
     *
     * @param roomCode
     */

    @Transactional(rollbackFor = Exception.class)
    protected abstract AbstractRoom<T, R, L> doAddRoom(String roomCode);

    /**
     * 加载房间内所有信息
     *
     * @param roomCode
     * @return
     */
    protected abstract AbstractRoom<T, R, L> load(String roomCode);

    /**
     * 当前用户加载房间内所有信息
     *
     * @param roomCode
     * @return
     */

    protected abstract AbstractRoom<T, R, L> currentLoad(String roomCode, BigDecimal userId);


    public abstract AbstractRoom<T, R, L> desensitize(AbstractRoom<MahjongRoomRule, MahjongRoomInfo, GameRuMahjongLogs> roomInfo, String roomCode, BigDecimal userId);


    /**
     * 退出房间
     *
     * @param room
     * @return
     */

    public abstract void exitRoom(GameRuRoom room, BigDecimal userId);

    public abstract void cleanRoom(GameRuRoom room);

    /**
     * TODO
     * 这里需要判断房间号是否存在
     * 如果存在了需要重新生成
     *
     * @param roomInfo
     */
    public final GameRuRoom create(AbstractRoom<?, ?, ?> roomInfo) {
        int roomCode = (int) ((Math.random() * 9 + 1) * 100000);
        roomInfo.setCode(String.valueOf(roomCode));
        roomInfo.setCreateDate(new Date());
        roomInfo.setCreater(CurrentUser.getUsername());
        roomInfo.setUserId(CurrentUser.getUserId());
        String metaStr = JacksonStringUtil.objectToString(roomInfo.getRoomRule(), false);
        T metaData = JacksonStringUtil.objectCase(metaStr, getFirst());
        GameRuRoom ruRoom = doCreate(String.valueOf(roomCode), roomInfo.getType(), metaData);
        return ruRoom;
    }

    public final AbstractRoom<T, R, L> addRoom(String roomCode) {
        AbstractRoom<T, R, L> result = doAddRoom(roomCode);
        return result;
    }

    public final AbstractRoom<T, R, L> loadRoom(String roomCode) {
        return load(roomCode);
    }

    public final AbstractRoom<T, R, L> currentLoadRoom(String roomCode, BigDecimal userId) {
        return currentLoad(roomCode, userId);
    }
}
